﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace PFDImageStudio
{
    public partial class Form1 : Form
    {
        Bitmap bmpHeadingIndicator;
        Bitmap bmpTemp;
        Bitmap bmpAirspeedStrip;
        Bitmap bmpIndicators;
        Bitmap bmpHorizon;
        Font myfont;
        Font myHorizonFont;
        Font indicatorFont;
        Color color1, color2;
        public Form1()
        {
            InitializeComponent();

            myfont = new Font("Courier", 12, FontStyle.Regular);
            indicatorFont = new Font("Courier", 12, FontStyle.Regular);
            myHorizonFont = new Font("Courier", 8, FontStyle.Regular);

            color1 = Color.White;
            color2 = Color.Green;

            DrawHeadingIndicator();
            DrawAirspeedStrip();
            DrawIndicator();
            DrawHorizon();
        }

        private void DrawHorizon()
        {
            int size = Convert.ToInt32(HorizonSize.Value);
            int width = Convert.ToInt32(HorizonWidth.Value);

            if (bmpHorizon != null)
                bmpHorizon.Dispose();

            bmpHorizon = new Bitmap(size, size, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            using (Graphics g = Graphics.FromImage(bmpHorizon))
            {
                //  Draw Horizon
                int nVertCenter = (size - 2) / 2;
                g.FillRectangle(new SolidBrush(Color.FromArgb(0, 153, 204)), new Rectangle(0, 0, width, (size - 2) / 2));
                g.DrawLine(Pens.White, 0, nVertCenter, width-1, nVertCenter);
                g.FillRectangle(new SolidBrush(Color.FromArgb(153, 102, 0)), new Rectangle(0, (size - 2) / 2 + 1, width, (size - 2) / 2));

                //  Draw Vertical Scale
                g.FillRectangle(new SolidBrush(Color.Transparent/* Color.FromArgb(8, 8, 8)*/), new Rectangle(width, 0, width, size));
                int heightTotal = nVertCenter;
                int heightAvail = heightTotal - width;
                float fPixPerAngle = (float)heightAvail / 90.0f;
                int nTickWidth10 = Convert.ToInt32(TickWidth10.Value);
                int nTickWidth5 = Convert.ToInt32(TickWidth5.Value);
                int nTickWidth2 = Convert.ToInt32(TickWidth2.Value);
                int nHorzCenter = width + width / 2;
                for (float i = -90.0f; i <= 90.0f; i+=2.5f )
                {
                    PointF ptLeft = new PointF((float)(nHorzCenter), (float)nVertCenter - fPixPerAngle * i);
                    PointF ptRight = new PointF((float)(nHorzCenter), (float)nVertCenter - fPixPerAngle * i);
                    if (((i % 5.0f) == 2.5f) || ((i % 5.0f) == -2.5f))
                    {
                        if ((i > -30.0f) && (i < 30.0f))
                        {
                            ptLeft.X -= nTickWidth2;
                            ptRight.X += nTickWidth2;
                            g.DrawLine(Pens.White, ptLeft, ptRight);
                        }
                    }
                    else
                        if (((i % 10.0f) == 5.0f) || ((i % 10.0f) == -5.0f))
                        {
                            ptLeft.X -= nTickWidth5;
                            ptRight.X += nTickWidth5;

                            g.DrawLine(Pens.White, ptLeft, ptRight);
                        }
                        else
                        {
                            ptLeft.X -= nTickWidth10;
                            ptRight.X += nTickWidth10;

                            if (i != 0.0f)
                            {
                                string s = i.ToString("F0");
                                StringFormat fmt = new StringFormat();
                                fmt.Alignment = StringAlignment.Far;
                                fmt.LineAlignment = StringAlignment.Center;
                                g.DrawString(s, myHorizonFont, Brushes.White, ptLeft, fmt);
                                fmt.Alignment = StringAlignment.Near;
                                g.DrawString(s, myHorizonFont, Brushes.White, ptRight, fmt);
                            }
                            g.DrawLine(Pens.White, ptLeft, ptRight);
                        }
                }
                
            }
        }

        private SizeF Max(SizeF s1, SizeF s2)
        {
            return new SizeF(Math.Max(s1.Width, s2.Width), Math.Max(s1.Height, s2.Height));
        }
        private void DrawIndicator()
        {
            int sz = (int)Indicator_Size.Value;

            if (bmpIndicators != null)
                bmpIndicators.Dispose();
            bmpIndicators = new Bitmap(sz, sz, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            using (Graphics g = Graphics.FromImage(bmpIndicators))
            {
                SizeF actSize;
                SolidBrush b1 = new SolidBrush(color1);
                SolidBrush b2 = new SolidBrush(color2);

                g.Clear(Color.Black);
                actSize = g.MeasureString("SPEED", indicatorFont);
                actSize = Max(g.MeasureString("ALT", indicatorFont), actSize);
                actSize = Max(g.MeasureString("G/S", indicatorFont), actSize);
                actSize = Max(g.MeasureString("HDG", indicatorFont), actSize);
                actSize = Max(g.MeasureString("LOC", indicatorFont), actSize);
                actSize = Max(g.MeasureString("AP1", indicatorFont), actSize);
                actSize = Max(g.MeasureString("1FD2", indicatorFont), actSize);
                actSize = Max(g.MeasureString("A/THR", indicatorFont), actSize);

                actSize.Width+=2;
                actSize.Height+=2;
                actSize.Width = (float)Math.Ceiling(actSize.Width);
                actSize.Height = (float)Math.Ceiling(actSize.Height);
                RectangleF r = new RectangleF(new PointF(0.0f, 0.0f), actSize);
                StringFormat sfmt = new StringFormat();
                sfmt.Alignment = StringAlignment.Center;
                sfmt.LineAlignment = StringAlignment.Center;
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("SPEED", indicatorFont, b2, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("ALT", indicatorFont, b2, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("G/S", indicatorFont, b2, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("HDG", indicatorFont, b2, r, sfmt);

                r.Location = new PointF(0.0f, actSize.Height);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("SPEED", indicatorFont, Brushes.Black, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("ALT", indicatorFont, Brushes.Black, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("G/S", indicatorFont, Brushes.Black, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("HDG", indicatorFont, Brushes.Black, r, sfmt);

                r.Location = new PointF(0.0f, actSize.Height * 2);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("LOC", indicatorFont, b2, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("AP1", indicatorFont, b1, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("1FD2", indicatorFont, b1, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("A/THR", indicatorFont, b1, r, sfmt);

                r.Location = new PointF(0.0f, actSize.Height * 3);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("LOC", indicatorFont, Brushes.Black, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("AP1", indicatorFont, Brushes.Black, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("1FD2", indicatorFont, Brushes.Black, r, sfmt);
                r.Offset(actSize.Width, 0.0f);
                g.DrawRectangle(Pens.White, r.Left, r.Top, r.Width, r.Height);
                g.DrawString("A/THR", indicatorFont, Brushes.Black, r, sfmt);


                r.Location = new PointF(0.0f, actSize.Height * 5);
                g.DrawString("Width=" + (actSize.Width-2).ToString(), indicatorFont, Brushes.White, r.Location);
                r.Offset(0.0f, actSize.Height);
                g.DrawString("Height=" + (actSize.Height-2).ToString(), indicatorFont, Brushes.White, r.Location);

                g.Dispose();
            }
        }

        private void DrawAirspeedStrip()
        {
            int LineSpacing = Convert.ToInt32(AirspeedStripLineSpacingNum.Value);
            int ViewportX = Convert.ToInt32(AirspeedStripViewportXNum.Value);
            int ViewportY = Convert.ToInt32(AirspeedStripViewportYNum.Value);

            //  Calculate the vertical length of the strip first
            float lengthOfStrip;

            //  First calculate one half viewport as buffer at the upper side
            //  and add one half viewport as buffer at the lower side making
            //  up a total of one viewport size.
            lengthOfStrip = Convert.ToSingle(ViewportY);
            //  Now add the space required to draw from 000 knots to 400 knots
            //  where 5 knots are separated by the given tick distance
            lengthOfStrip += (400.0f / 5.0f) * Convert.ToSingle(LineSpacing);

            //  Now that we have the total length of the strip, we need to make it fit
            //  into a square bitmap. How do I achieve that ?
            //  I loop through all possible scenarios and find out the one that fits the best.
            //  Starting with the first size which is a power of 2 and greater than one viewport size
            float fSize = findNextBestPowerOf2Size(ViewportY);
            //  Calculate the best optimum area value
            float fOptimumArea = lengthOfStrip * Convert.ToSingle(ViewportX);

            System.Diagnostics.Debug.WriteLine("Strip height of " + lengthOfStrip.ToString() + " requested !");
            System.Diagnostics.Debug.WriteLine("Searching for optimum size !!!");
            float fBestRatio = 100000000000000.0f;
            float fBestSize = 0.0f;
            do
            {
                //  Calculate the width for the given vertical size
                float fStripLen = (float)(Math.Ceiling(lengthOfStrip / (fSize - Convert.ToSingle(ViewportY)))) * Convert.ToSingle(ViewportX);
//                float fStripLen = (float)(lengthOfStrip / (fSize - Convert.ToSingle(ViewportY))) * Convert.ToSingle(ViewportX);

                //  Calculate the effective area
                float fEffectiveSize = Math.Max(findNextBestPowerOf2Size(Convert.ToInt32(fStripLen)), fSize);
                float fEffectiveArea = fEffectiveSize * fEffectiveSize;

                if (fEffectiveArea / fOptimumArea < fBestRatio)
                {
                    System.Diagnostics.Debug.WriteLine("Effective Size (" + fEffectiveSize.ToString()
                        + "), Width (" + fStripLen.ToString()
                        + "), Size  (" + fSize.ToString() + ")");
                    System.Diagnostics.Debug.WriteLine("Effective Area is " + fEffectiveArea.ToString()
                        + " with ratio of " + (fEffectiveArea / fOptimumArea).ToString());
                    fBestRatio = fEffectiveArea / fOptimumArea;
                    fBestSize = fEffectiveSize;
                }

                fSize *= 2.0f;
            } while (fSize < lengthOfStrip);

            System.Diagnostics.Debug.WriteLine("Chose size of " + fBestSize.ToString());
            // Set optimum size
            AirspeedStripSizeNum.Value = Convert.ToDecimal(fBestSize);

            if (bmpAirspeedStrip != null)
                bmpAirspeedStrip.Dispose();
            bmpAirspeedStrip = new Bitmap(Convert.ToInt32(fBestSize), Convert.ToInt32(fBestSize), System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            if (bmpTemp != null)
                bmpTemp.Dispose();
            bmpTemp = new Bitmap(Convert.ToInt32(AirspeedStripViewportXNum.Value), Convert.ToInt32(lengthOfStrip), System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            using (Graphics g = Graphics.FromImage(bmpTemp))
            {
                g.Clear(Color.Gray);

                Pen p1 = new Pen(Brushes.White, Convert.ToSingle(AirspeedStripLineSizeNum.Value) / 2.0f);

                //  Define the start position
                for (int spd = 0; spd <= 400/5; spd++)
                {
                    if ((spd % 2) == 0)
                    {
                        float xleft = Convert.ToSingle(ViewportX) - 1.0f - Convert.ToSingle(AirspeedStripTickLenMaxNum.Value); 
                        float xright = Convert.ToSingle(ViewportX) - 1.0f;
                        float y = Convert.ToSingle(ViewportY / 2 + spd * LineSpacing);
                        //  Draw max ticks
                        g.DrawLine(p1, xright, y, xleft, y);

                        if ((spd % 4) == 0)
                        {
                            string s;
                            s = string.Format("{0:D3}", 400 - spd * 5);
                            StringFormat fmt = new StringFormat();
                            fmt.Alignment = StringAlignment.Far;
                            fmt.LineAlignment = StringAlignment.Center;
                            g.DrawString(s, myfont, Brushes.White, new PointF(xleft, y), fmt);
                        }
                    }
                    else
                    {
                        if ((spd*5) >= 200 )
                        {
                            float xleft = Convert.ToSingle(ViewportX) - 1.0f - Convert.ToSingle(AirspeedStripTickLenMinNum.Value);
                            float xright = Convert.ToSingle(ViewportX) - 1.0f;
                            float y = Convert.ToSingle(ViewportY / 2 + spd * LineSpacing);
                            //  Draw min ticks
                            g.DrawLine(p1, xright, y, xleft, y);
                        }
                    }
                }
            }

            //  Now the long item is drawn and we need to blit into the optimal bitmap
            using (Graphics gr = Graphics.FromImage(bmpAirspeedStrip))
            {
                gr.Clear(Color.White);
            
                float f = 0;
                float x = 0.0f;
                while((lengthOfStrip - f) >= fBestSize)
                {
                    RectangleF rDest = new RectangleF(x, 0.0f, Convert.ToSingle(ViewportX), fBestSize);
                    RectangleF rSrc  = new RectangleF(0.0f, f, Convert.ToSingle(ViewportX), fBestSize);

                    gr.DrawImage(bmpTemp, rDest, rSrc, GraphicsUnit.Pixel);

                    f += (fBestSize - Convert.ToSingle(ViewportY));
                    x += Convert.ToSingle(ViewportX);
                }
                if ((lengthOfStrip-f) != 0)
                {
                    RectangleF rSrc = new RectangleF(0.0f, f, Convert.ToSingle(ViewportX), lengthOfStrip-f);
                    gr.DrawImage(bmpTemp, x, 0.0f, rSrc, GraphicsUnit.Pixel);
                }
            }
        }

        private float findNextBestPowerOf2Size(int p)
        {
            Int32 z = p;
            int c = 31;

            int mask = (1 << c);
            while ((z & (1<<(--c))) == 0) ;

            return Convert.ToSingle(1 << (++c));
        }

        private void DrawHeadingIndicator()
        {
            float size = Convert.ToSingle(sizeNum.Value);
            float offs = Convert.ToSingle(offsetNum.Value);

            if (bmpHeadingIndicator == null)
            {
                bmpHeadingIndicator = new Bitmap(Convert.ToInt32(size), Convert.ToInt32(size), System.Drawing.Imaging.PixelFormat.Format32bppArgb);

            }
            using (Graphics g = Graphics.FromImage(bmpHeadingIndicator))
            {
                g.Clear(Color.White);
                Pen p1 = new Pen(Brushes.White, Convert.ToSingle(penSizeNum.Value)/2.0f);
                //  First fill with transparent background
                g.FillRectangle(Brushes.Black, 0, 0, size, size);

                //  Now Draw the circle shaped plate
                g.DrawEllipse(p1, offs, offs, size-2*offs, size-2*offs);

                //  Draw the inner dashed circle
                using (Pen p = new Pen(Brushes.White, Convert.ToSingle(penSizeNum.Value) / 2.0f))
                {
                    float[] dashValues = {5, 5, 5, 5};
                    p.DashPattern = dashValues;
                    int xy = Convert.ToInt32( offs + Convert.ToSingle(innerCircleNum.Value));
                    p.DashStyle = System.Drawing.Drawing2D.DashStyle.Custom;
                    g.DrawEllipse(p, xy, xy, size - 2*xy, size - 2*xy); 
                }
                float fCenter = Convert.ToSingle(size) / 2.0f;
                float fRadiusInner = Convert.ToSingle(size)/2.0f -Convert.ToSingle(offs);
                float fRadiusShortOuter = fRadiusInner + Convert.ToSingle(smallStipNum.Value);
                float fRadiusLongOuter = fRadiusInner + Convert.ToSingle(longStipNum.Value);
                float fPositionRadius = fRadiusInner + Convert.ToSingle(numberOffsetNum.Value);

                //  Draw the little cross in the center
                float nnn = Convert.ToSingle(innerCrossNum.Value);
                g.DrawLine(p1, fCenter - nnn, fCenter, fCenter + nnn, fCenter);
                g.DrawLine(p1, fCenter, fCenter - nnn, fCenter, fCenter + nnn);

                //  Now Draw the directions
                for (int i = 0; i < 72; i++)
                {
                    double fAngle = Math.PI*(i * 5)/180.0f;
                    float fInnerx, fInnery, fOuterx, fOutery;

                    fInnerx = fCenter + fRadiusInner * (float)Math.Sin(fAngle);
                    fInnery = fCenter - fRadiusInner * (float)Math.Cos(fAngle);

                    if ((i % 2) == 0)
                    {
                        fOuterx = fCenter + fRadiusLongOuter * (float)Math.Sin(fAngle);
                        fOutery = fCenter - fRadiusLongOuter * (float)Math.Cos(fAngle);
                    }
                    else
                    {
                        fOuterx = fCenter + fRadiusShortOuter * (float)Math.Sin(fAngle);
                        fOutery = fCenter - fRadiusShortOuter * (float)Math.Cos(fAngle);
                    }

                    if ((i % 6) == 0)
                    {
                        int iAngle = i / 2;

                        g.TranslateTransform(fCenter, fCenter);
                        g.RotateTransform((float)Convert.ToSingle(i * 5));
                        g.TranslateTransform(0, -fPositionRadius);
                        StringFormat fmt = new StringFormat();
                        fmt.Alignment = StringAlignment.Center;
                        fmt.LineAlignment = StringAlignment.Far;
                        g.DrawString(iAngle.ToString(), myfont, Brushes.White, new PointF(0, 0), fmt);

                        g.ResetTransform();
                    }

                    g.DrawLine(p1, fInnerx, fInnery, fOuterx, fOutery);
                }
            }
        }

        private void RedrawHeadingIndicator(object sender, EventArgs e)
        {
            DrawHeadingIndicator();
            PreviewPanel.Invalidate();
            PreviewPanel.Update();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            fontDialog.Font = myfont;
            if (fontDialog.ShowDialog() == DialogResult.OK)
            {
                myfont = fontDialog.Font;
                DrawHeadingIndicator();
                PreviewPanel.Invalidate();
                PreviewPanel.Update();
            }

        }

        private void sizeNum_ValueChanged(object sender, EventArgs e)
        {
            if (bmpHeadingIndicator != null)
                bmpHeadingIndicator.Dispose();
            bmpHeadingIndicator = new Bitmap((int)sizeNum.Value, (int)sizeNum.Value, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            DrawHeadingIndicator();
            PreviewPanel.Invalidate();
            PreviewPanel.Update();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                if (tabControl1.SelectedTab.Name == "CompassPage")
                    bmpHeadingIndicator.Save(saveFileDialog.FileName);
                else
                    if (tabControl1.SelectedTab.Name == "AirspeedStripPage")
                        bmpAirspeedStrip.Save(saveFileDialog.FileName);
                    else
                        if (tabControl1.SelectedTab.Name == "IndicatorPage")
                            bmpIndicators.Save(saveFileDialog.FileName);
                        else
                            if (tabControl1.SelectedTab.Name == "HorizonPage")
                                bmpHorizon.Save(saveFileDialog.FileName);
            }
        }

        private void RedrawAirspeedStrip(object sender, EventArgs e)
        {
            DrawAirspeedStrip();
            PreviewPanel.Invalidate();
            PreviewPanel.Update();
        }


        private void IndicatorFont_Click(object sender, EventArgs e)
        {
            fontDialog.Font = indicatorFont;
            if (fontDialog.ShowDialog() == DialogResult.OK)
            {
                indicatorFont = fontDialog.Font;
                DrawIndicator();
                PreviewPanel.Invalidate();
                PreviewPanel.Update();
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            colorDialog1.Color = color1;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                color1 = colorDialog1.Color;
                PreviewPanel.Invalidate();
                PreviewPanel.Update();
            }
        }

        private void button4_Click(object sender, EventArgs e)
        {
            colorDialog1.Color = color2;
            if (colorDialog1.ShowDialog() == DialogResult.OK)
            {
                color2 = colorDialog1.Color;
                PreviewPanel.Invalidate();
                PreviewPanel.Update();
            }
        }

        private void RedrawHorizon(object sender, EventArgs e)
        {
            DrawHorizon();
            PreviewPanel.Invalidate();
            PreviewPanel.Update();
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            PreviewPanel.Invalidate();
            PreviewPanel.Update();
        }

        private void PreviewPanel_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.Clear(Color.Black);

            switch (tabControl1.SelectedIndex)
            {
                case 0:
                    e.Graphics.DrawImage(bmpHeadingIndicator, 0, 0);
                    break;

                case 1:
                    e.Graphics.DrawImage(bmpAirspeedStrip, 0, 0);
                    break;

                case 2:
                    e.Graphics.DrawImage(bmpIndicators, 0, 0);
                    break;

                case 3:
                    e.Graphics.DrawImage(bmpHorizon, 0, 0);
                    break;
            }
        }

        private void button5_Click(object sender, EventArgs e)
        {
            fontDialog.Font = myHorizonFont;
            if (fontDialog.ShowDialog() == DialogResult.OK)
            {
                myHorizonFont = fontDialog.Font;
                DrawHorizon();
                PreviewPanel.Invalidate();
                PreviewPanel.Update();
            }
        }
    }
}
